[AWS AppSync] エッジデバイスのデータを AWS IoT経由で取得してGraphQLを更新してみました。
1 はじめに
CX事業本部の平内(SIN)です。
AWS AppSync(以下、AppSync)は、GraphQLを利用するためのマネージド型サービスです。 今回は、このGraphQLへのMutationをAWS IoT経由で取得したデータで行うものを試してみました。
最初に動作している様子です。
デバイスからのデータをiOSで表示するだけなら、MQTTのPublishとSubscribeだけで充分だと思うのですが、ここは、今後の拡張を視野に入れて、敢えてGraphQLを挟むという事にさせて下さい。
2 構成
構成は、以下のとおりです。
エッジデバイスのデータは、MQTTでPublishされています。AWS IoTでは、ルールにより、到着したデータをすべてLambdaに送っています。
AppSyncへのMutationは、Lambdaから行われ、iOSでは、AWS Amplifyを使用して、そのデータをSubscriptionしてリアルタイムに表示しています。
なお、AppSyncのデータストアは、DynamoDBとし、認証は、APIキーのみです。
3 AppSync
AppSyncでアプリ(AppSyncSample)を作成し、スキーマーを以下のように必要最小限としました。
type Data { id: String! datetime: AWSDateTime value: Float } type Mutation { createData(value: Float!, datetime: AWSDateTime!): Data } type Query { getData(id: String!): Data } type Subscription { onCreateData: Data @aws_subscribe(mutations: ["createData"]) }
クエリ画面でmutationの動作を確認している様子です。
上記のクエリで、データストアとして設定したDynamoDBにデータが追加されていることを確認できます。
4 データの生成
エッジデバイスでは、下記の形式のデータを生成します。
{ "value": 64.2, "datetime": 1578878926168 }
MQTTでPublishしているコードの抜粋は、以下のとおりです。生成されたデータを300msecごとに送っています。
"use strict"; var awsIot = require('aws-iot-device-sdk'); class Mqtt { constructor(){ this.topic = 'topic_1'; this.device = awsIot.device({ keyPath: './private.pem', certPath: './cert.pem', caPath: './root-CA.crt', clientId: 'iot-data-generator', host: 'xxxxxxxxxx.iot.ap-northeast-1.amazonaws.com' }); } async connect(){ return new Promise( (resolve,reject) =>{ this.device.on('connect', function() { console.log('connected'); resolve(); }); }); } publish(value){ const date = new Date(); const json = { value: value, datetime: date.getTime() } console.log(JSON.stringify(json)) this.device.publish(this.topic, JSON.stringify(json)); } } async function main() { const mqtt = new Mqtt(); await mqtt.connect(); while(true){ const value = await adconv.read(); // 生成されたデータの取得 mqtt.publish(value); await sleep(300); } } main();
エッジデバイスについての細部は、下記のブログ記事をご参照ください。
[AWS IoT] AWS IoTのデバッグ用にデータジェネレーターを作ってみました
5 AWS IoT
AWS IoTでは、下記のような簡単なルールを設定して、トピックに到着したデータをそのまま、Lambda(AppSyncSample)に送っています。
SELECT * FROM 'topic_1'
6 Lambda
LambdaでAppSyncにMutationしているコードは、以下のとおりです。
先のルールで起動されるLambdaでは、パラメータ(event)に、Publishされたデータがそのまま入っていますので、GraphQLで定義したスキーマの型に変換を行っています。
なお、クエリは、先程、AppSyncのコンソールで動作確認したクエリをそのままコピーしています。
AppSyncへの認証は、APIキーとなっているので、特別なパーミッションは必要ありません。
index.js
require('isomorphic-fetch'); const AWSAppSyncClient = require('aws-appsync').default; const gql = require('graphql-tag'); // クエリ const query = gql(`mutation CreateData($value: Float!, $datetime: AWSDateTime!){ createData(value: $value, datetime: $datetime){ id value datetime } }`); exports.handler = async (event: any) => { // event = { // "value": 23.5, // "datetime": 1578879458040 // } const url = process.env.END_POINT; const region = process.env.REGION; const apiKey = process.env.API_KEY; const authType = 'API_KEY'; const client = new AWSAppSyncClient({ url: url, region: region, auth: { type: authType, apiKey: apiKey }, disableOffline: true, fetchPolicy: 'network-only' }); if(event.value && event.datetime){ const dt = new Date(event.datetime); const params = { "datetime": dt.toISOString(), // AWSDateTimeへの変換 2011-10-05T14:48:00.000Z "value": parseFloat(event.value) // Floatへの変換 } try { await client.mutate({ variables: params, mutation: query }); } catch (err) { console.log(JSON.stringify(err)); } } }
Lambdaから、GraphQLへのクエリについての詳しい説明は、下記をご参照ください。
[AWS AppSync] LambdaからGraphQLのクエリを送ってみる
7 iOS
Xcodeでプロジェクトを作成した後の手順は、以下のとおりです。(コマンドは、プロジェクトのトップで実行しています)
※プログレスバーは、KDCircularProgressを利用させて頂きました。
(1) Cocoapods
CocoaPodsでAWSAppSyncとKDCircularProgressを追加しています。
$ pod init $ vi Podfile target 'AppSyncSample' do use_frameworks! pod 'AWSAppSync', '~> 2.15.0' pod 'KDCircularProgress' end $ pod install
(2) Amplify初期化
amplify initでAmplifyの初期化(CFnのスタック生成等)を行います。
$ amplify init
(3) GrapQLドキュメントの生成
amplify add codegenでiOSからアクアセスするためのクラスが生成されます。
$ amplify add codegen --apiId xxxxxxxxxxxxxxxxxxxxx
また、 amplify pushして、反映させて置きます。
$ amplify push $ amplify status Current Environment: demo | Category | Resource name | Operation | Provider plugin | | -------- | ------------- | --------- | --------------- | | Api | AppSyncSample | Create | |
生成されたファイル(awsconfiguration.json及び、API.swift)は、プロジェクトに追加します。
(4) AWSAppSyncClientのインスタンス生成
下記は、AppDelegate.swiftで、AWSAppSyncClientのインスタンスを生成しているコードの抜粋です。
import UIKit import AWSAppSync @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var appSyncClient: AWSAppSyncClient? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { do { let cacheConfiguration = try AWSAppSyncCacheConfiguration() let appSyncConfig = try AWSAppSyncClientConfiguration(appSyncServiceConfig: AWSAppSyncServiceConfig(), cacheConfiguration: cacheConfiguration) appSyncClient = try AWSAppSyncClient(appSyncConfig: appSyncConfig) } catch { print("Error initializing appsync client. \(error)") } return true }
(5) subscriptionによるデータ取得
API.swift で提供されるクラスでGraphQLにアクセス可能になります。リアルタイムでデータを取得して、プログレスバーを更新しているコードの一部です。
class ViewController: UIViewController { var appSyncClient: AWSAppSyncClient? @IBOutlet weak var progress: KDCircularProgress! @IBOutlet weak var label: UILabel! override func viewDidLoad() { super.viewDidLoad() guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } appSyncClient = appDelegate.appSyncClient subscribe() } func setValue(value:Double) { self.progress.angle = value/100 * 360.0 self.label.text = String(value) } var discard: Cancellable? func subscribe() { do { discard = try appSyncClient?.subscribe(subscription: OnCreateDataSubscription(), resultHandler: { (result, transaction, error) in if let result = result { self.setValue(value: result.data?.onCreateData?.value as! Double) } else if let error = error { print(error.localizedDescription) } }) } catch { print("Error starting subscription.") } } }
AmprifyによるiOSからのGraphQLへのアクセスについては、下記を参考にさせて頂きました。
参考:https://aws-amplify.github.io/docs/sdk/ios/start
8 最後に
今回は、AppSyncを使用して、エッジデバイスのデータ変化をiOSで表示してみました。実際に物理的な動作(ボリュームを回す)が、リアルタイムに遠隔表示できると、ちょっと、気持ち良かったです。
すべてのコードは、下記に置きました。
https://github.com/furuya02/AppSyncSample